﻿Imports System.Drawing.Drawing2D
Imports System.Net
Imports System.Net.Sockets
Imports System.Text

Module HarryNet
    Public Enum HarryDataType
        NONE = 0
        BINARY = 1
        STR = 2
        ENCRYPTION = 4
    End Enum
    Public Class HarryReceiveData
        Public Property Completed As Boolean
        Public Property TempData As New List(Of Byte)
        Public Property DataType As HarryDataType = HarryDataType.NONE
        Public Property FullDataLength As Long = -1
        Public Event OnReceivedData(data As Byte(), dataType As HarryDataType)
        Public Sub ProcessReceivedData(data As Byte())
            If data.LongLength > 0 Then
                TempData.AddRange(data)
            End If
            If FullDataLength = -1 Then
                '准备解析实体数据长度
                If TempData.LongCount < 8 Then
                    Return
                End If
                Dim encodedData As Byte() = TempData.GetRange(0, 8).ToArray
                If BitConverter.IsLittleEndian Then
                    Array.Reverse(encodedData)
                End If
                FullDataLength = BitConverter.ToInt64(encodedData, 0)
                TempData.RemoveRange(0, 8)
                ProcessReceivedData({})
            ElseIf DataType = HarryDataType.NONE Then
                '准备解析数据类型
                If TempData.LongCount < 4 Then
                    Return
                End If
                Dim encodedData As Byte() = TempData.GetRange(0, 4).ToArray
                If BitConverter.IsLittleEndian Then
                    Array.Reverse(encodedData)
                End If
                DataType = CType(BitConverter.ToInt32(encodedData, 0), HarryDataType)
                TempData.RemoveRange(0, 4)
                ProcessReceivedData({})
            ElseIf FullDataLength <> -1 Then
                '收集实体数据
                If TempData.LongCount < FullDataLength Then
                    Return
                End If
                Dim encodedData As Byte() = TempData.GetRange(0, FullDataLength).ToArray
                TempData.RemoveRange(0, FullDataLength)
                RaiseEvent OnReceivedData(encodedData, DataType)
                '实体数据收集完毕，抛出数据进入下次收集
                FullDataLength = -1
                DataType = HarryDataType.NONE
                ProcessReceivedData({})
            End If
        End Sub
    End Class
    Public Class HarrySendData
        Public Property Data As Byte()
        Public Sub New(data As Byte(), dataType As HarryDataType)
            ReDim Me.Data(data.LongLength + 11)
            Dim dataLength As Long = data.LongLength
            Dim lengthBytes = BitConverter.GetBytes(dataLength)
            If BitConverter.IsLittleEndian Then
                Array.Reverse(lengthBytes)
            End If
            Array.Copy(lengthBytes, 0, Me.Data, 0, 8)
            Dim typeBytes = BitConverter.GetBytes(CInt(dataType))
            If BitConverter.IsLittleEndian Then
                Array.Reverse(typeBytes)
            End If
            Array.Copy(typeBytes, 0, Me.Data, 8, 4)
            If data IsNot Nothing AndAlso dataLength > 0 Then
                Array.Copy(data, 0, Me.Data, 12, dataLength)
            End If
        End Sub
    End Class
    Public Class HarrySocket
        Public Property MainSocket As Socket
        Public Property ParentServer As HarrySocketServer
        Public Property ParentClient As HarrySocketClient
        Public Property Alive As Boolean = True
        Public Property ID As Integer
        Private ReadOnly sendQueue As New Queue(Of Byte())
        Private isSending As Boolean = False
        Private ReadOnly sendLock As New Object()
        Private ReadOnly receiveBuffer As Byte() = New Byte(8191) {}
        Private ReadOnly receiveEventArgs As New SocketAsyncEventArgs
        Private WithEvents LastReceiveData As New HarryReceiveData
        Public Event OnReceivedData(id As Integer, data As Byte(), dataType As HarryDataType)
        Public Sub New(parentClient As HarrySocketClient, acceptSocket As Socket)
            MainSocket = acceptSocket
            Me.ParentClient = parentClient
            StartReceiving()
        End Sub
        Public Sub New(parentServer As HarrySocketServer, id As Integer, acceptSocket As Socket)
            MainSocket = acceptSocket
            Me.ParentServer = parentServer
            Me.ID = id
            StartReceiving()
        End Sub
        Public Sub ReceivedData(data As Byte(), dataType As HarryDataType) Handles LastReceiveData.OnReceivedData
            RaiseEvent OnReceivedData(ID, data, dataType)
        End Sub
        Public Sub StartReceiving()
            receiveEventArgs.SetBuffer(receiveBuffer, 0, receiveBuffer.Length)
            AddHandler receiveEventArgs.Completed, AddressOf OnReceiveCompleted
            MainSocket.ReceiveAsync(receiveEventArgs)
        End Sub
        Private Sub OnReceiveCompleted(sender As Object, e As SocketAsyncEventArgs)
            If e.SocketError = SocketError.Success AndAlso e.BytesTransferred > 0 Then
                ' 处理收到的数据：e.Buffer, offset=0, count=e.BytesTransferred
                Dim receivedData As Byte() = New Byte(e.BytesTransferred - 1) {}
                Array.Copy(e.Buffer, 0, receivedData, 0, e.BytesTransferred)
                ProcessReceivedData(receivedData)
                e.SetBuffer(0, receiveBuffer.Length) ' 重置偏移和长度（缓冲区可复用）
                If Not MainSocket.ReceiveAsync(e) Then
                    OnReceiveCompleted(Nothing, e)
                End If
            Else
                ' 连接关闭或出错
                Close()
            End If
        End Sub
        Private Sub ProcessReceivedData(data As Byte())
            LastReceiveData.ProcessReceivedData(data)
        End Sub
        Private Sub BeginSend()
            Dim data As Byte() = sendQueue.Dequeue()
            Dim e As New SocketAsyncEventArgs()
            e.SetBuffer(data, 0, data.Length)
            AddHandler e.Completed, AddressOf OnSendCompleted
            If Not MainSocket.SendAsync(e) Then
                OnSendCompleted(Nothing, e)
            End If
        End Sub
        Public Sub Send(data As Byte(), Optional dataType As HarryDataType = HarryDataType.BINARY)
            SyncLock sendLock
                sendQueue.Enqueue(New HarrySendData(data, dataType).Data) ' 防止外部修改
                If Not isSending Then
                    isSending = True
                    BeginSend()
                End If
            End SyncLock
        End Sub

        Public Sub Send(data As String)
            Send(Encoding.UTF8.GetBytes(data), HarryDataType.STR)
        End Sub

        Private Sub OnSendCompleted(sender As Object, e As SocketAsyncEventArgs)
            Try
                If e.SocketError <> SocketError.Success Then
                    ' 处理错误，例如记录日志或关闭连接
                    Console.WriteLine($"Send failed: {e.SocketError}")
                    ' 可选：关闭 socket
                    Close()
                Else
                    ' 发送成功，可以在这里触发后续逻辑
                    Console.WriteLine($"Sent {e.BytesTransferred} bytes.")
                End If
            Finally
                ' 释放事件参数资源
                e.Dispose()
            End Try
            If Alive Then
                SyncLock sendLock
                    If sendQueue.Count > 0 Then
                        BeginSend() ' 继续发送下一条
                    Else
                        isSending = False ' 发送队列空了
                    End If
                End SyncLock
            End If
        End Sub
        Public Sub Close()
            Alive = False
            ParentServer?.AllClients.Remove(ID)
            MainSocket?.Close()
        End Sub
    End Class
    Public Class HarrySocketServer
        Private Property ListenSockt As Socket
        Public Property AllClients As New Dictionary(Of Integer, HarrySocket)
        Public Event OnAddClient(id As Integer)
        Private Property IDCount As Integer
        Public Event OnReceivedData(id As Integer, data As Byte(), dataType As HarryDataType)
        Public Sub StartListen(Optional port As Integer = 6696)
            ListenSockt = New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
            ListenSockt.Bind(New IPEndPoint(IPAddress.Any, port))
            ListenSockt.Listen(10)
            Dim e As New SocketAsyncEventArgs
            ListenSockt.AcceptAsync(e)
            AddHandler e.Completed, AddressOf OnAccept
        End Sub
        Private Function GetClientID() As Integer
            IDCount += 1
            Return IDCount
        End Function
        Public Sub OnAccept(sender As Object, e As SocketAsyncEventArgs)
            If e.SocketError <> SocketError.Success Then
                ' 可选：记录错误
                ListenSockt.AcceptAsync(e)
                Return
            End If
            Dim id As Integer = GetClientID()
            Dim client As New HarrySocket(Me, id, e.AcceptSocket)
            AllClients.Add(id, client)
            e.AcceptSocket = Nothing
            ListenSockt.AcceptAsync(e)
            AddHandler client.OnReceivedData, AddressOf OnReceivedDataFromClient
            RaiseEvent OnAddClient(id)
        End Sub
        Private Sub OnReceivedDataFromClient(id As Integer, data As Byte(), dataType As HarryDataType)
            RaiseEvent OnReceivedData(id, data, dataType)
        End Sub
        Public Sub SendDataToClient(id As Integer, data() As Byte, Optional dataType As HarryDataType = HarryDataType.BINARY)
            Dim client As HarrySocket
            If AllClients.TryGetValue(id, client) Then
                client.Send(data, dataType)
            End If
        End Sub
        Public Sub SendDataToAllClient(data() As Byte, Optional dataType As HarryDataType = HarryDataType.BINARY)
            Broadcast(data, dataType)
        End Sub
        Public Sub SendDataToAllClient(data As String)
            Broadcast(Encoding.UTF8.GetBytes(data), HarryDataType.STR)
        End Sub
        Public Sub Broadcast(data() As Byte, Optional dataType As HarryDataType = HarryDataType.BINARY)
            For Each client As HarrySocket In AllClients.Values
                client.Send(data, dataType)
            Next
        End Sub
    End Class
    Public Class HarrySocketClient
        Public Property MainSocket As HarrySocket
        Public Event OnReceivedData(data As Byte(), dataType As HarryDataType)
        Public Sub StartConnect(Optional ip As String = "127.0.0.1", Optional port As Integer = 6696)
            Dim sk As New Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)
            Dim e As New SocketAsyncEventArgs With {
                .RemoteEndPoint = New IPEndPoint(IPAddress.Parse(ip), port)
            }
            sk.ConnectAsync(e)
            AddHandler e.Completed, AddressOf OnConnect
        End Sub
        Public Sub SendData(data As Byte(), Optional dataType As HarryDataType = HarryDataType.BINARY)
            MainSocket.Send(data, dataType)
        End Sub
        Public Sub SendData(data As String)
            MainSocket.Send(Encoding.UTF8.GetBytes(data), HarryDataType.STR)
        End Sub
        Private Sub OnConnect(sender As Object, e As SocketAsyncEventArgs)
            MainSocket = New HarrySocket(Me, e.ConnectSocket)
            AddHandler MainSocket.OnReceivedData, AddressOf OnReceivedDataFromServer
        End Sub
        Private Sub OnReceivedDataFromServer(id As Integer, data As Byte(), dataType As HarryDataType)
            RaiseEvent OnReceivedData(data, dataType)
        End Sub
    End Class
End Module
